#!/usr/bin/env ruby

# Copyright (c) 2023-2024 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# NB: put the functions generation to the ETS plugin

include_relative 'array_helpers.irt'

macro(:do_checks_ets) do
    src_size := LoadI(src_obj).Imm(Constants::ARRAY_LENGTH_OFFSET).u32
    dst_size := LoadI(dst_obj).Imm(Constants::ARRAY_LENGTH_OFFSET).u32

    If(src_start, src_end).GT.Unlikely.b {
        Goto(:FallToIntrinsic)
    }

    If(src_end, src_size).GT.Unlikely.b {
        Goto(:FallToIntrinsic)
    }

    IfImm(Compare(src_start, 0).LT.b).Imm(0).NE.Unlikely.b {
        Goto(:FallToIntrinsic)
    }

    IfImm(Compare(dst_start, 0).LT.b).Imm(0).NE.Unlikely.b {
        Goto(:FallToIntrinsic)
    }
    
    data_length := Sub(src_end, src_start).u32
    available_space := Sub(dst_size, dst_start).u32
    IfImm(Compare(data_length, available_space).GT.b).Imm(0).NE.Unlikely.b {
        Goto(:FallToIntrinsic)
    }
end

def GenerateArrayCopyTo(el_size)
    suffix = el_size.to_s + "B"
    lowercase_suffix = el_size.to_s + "b"
    #$regmask = Options.arch_64_bits? ? $temps_mask + :arg0 + :arg1 + :arg2 + :arg3 + :arg4 : $panda_mask
    $regmask = $panda_mask
    function("ArrayCopyTo#{suffix}".to_sym,
          params: {src_obj: 'ref', dst_obj: 'ref', dst_start: 'u32', src_start: 'u32', src_end: 'u32'},
          regmap: $full_regmap,
          regalloc_set: $regmask,
          mode: [:FastPath]) {

    # Arm32 is not supported
    if Options.arch == :arm32
        Intrinsic(:UNREACHABLE).void.Terminator
        next
    end


if defines.PANDA_WITH_ETS
    do_checks_ets

    len := Sub(src_end, src_start).u32;
    el_log_size = Math.log2(el_size).to_i
    do_array_copy(el_size, el_log_size, src_obj, dst_obj, src_start, dst_start, len)

    Goto(:End)

Label(:FallToIntrinsic)
    # call Intrinsic
    ep_offset = get_entrypoint_offset("ARRAY_COPY_TO#{suffix}_SLOW_PATH")
    Intrinsic(:SLOW_PATH_ENTRY, src_obj, dst_obj, dst_start, src_start, src_end).AddImm(ep_offset).MethodAsImm("ArrayCopyTo#{lowercase_suffix}OddSavedBridge").Terminator.void
    Intrinsic(:UNREACHABLE).Terminator.void if defines.DEBUG
end

Label(:End)
    ReturnVoid().void
}
end

GenerateArrayCopyTo(el_size = 1)
GenerateArrayCopyTo(el_size = 2)
GenerateArrayCopyTo(el_size = 4)
GenerateArrayCopyTo(el_size = 8)

def GenerateArrayCopyToUnchecked(el_size)
    suffix = el_size.to_s + "Byte"
    #$regmask = Options.arch_64_bits? ? $temps_mask + :arg0 + :arg1 + :arg2 + :arg3 + :arg4 : $panda_mask
    $regmask = $panda_mask
    function("ArrayCopyToUnchecked#{suffix}".to_sym,
          params: {src_obj: 'ref', dst_obj: 'ref', dst_start: 'u32', src_start: 'u32', src_end: 'u32'},
          regmap: $full_regmap,
          regalloc_set: $regmask,
          mode: [:FastPath]) {

    # Arm32 is not supported
    if Options.arch == :arm32
        Intrinsic(:UNREACHABLE).void.Terminator
        next
    end

    len := Sub(src_end, src_start).u32;
    el_log_size = Math.log2(el_size).to_i
    do_array_copy(el_size, el_log_size, src_obj, dst_obj, src_start, dst_start, len)
    ReturnVoid().void
}
end

GenerateArrayCopyToUnchecked(el_size = 1)
GenerateArrayCopyToUnchecked(el_size = 2)
GenerateArrayCopyToUnchecked(el_size = 4)
GenerateArrayCopyToUnchecked(el_size = 8)